package fwfxserver

import (
	"context"
	"encoding/json"
	"errors"
	authutils "gitee.com/zxs-micro/zxs-micro-auth/grpc/authserver/util"
	businessutil "gitee.com/zxs-micro/zxs-micro-business/grpc/businesses/utils"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/common"
	commonutil "gitee.com/zxs-micro/zxs-micro-common/grpc/common/utils"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/protos/utils"
	"gitee.com/zxs-micro/zxs-micro-common/log"
	"gitee.com/zxs-micro/zxs-micro-fuwufaxian/grpc/protos/fwfx"
	"gitee.com/zxs-micro/zxs-micro-fuwufaxian/model"
	gtewayutils "gitee.com/zxs-micro/zxs-micro-gateway/grpc/gateway/utils"
	"strings"
	"sync"
	"time"
)

var signalf *FwfxService
var signalm sync.Mutex

func NewFwfxService() *FwfxService {
	signalm.Lock()
	defer signalm.Unlock()
	if signalf == nil {
		signalf = new(FwfxService)
		signalf.FuwuMemList = make(map[string]map[string]model.Fuwu)
		signalf.startHealthCheck = false
	}
	return signalf
}

type FwfxService struct {
	lock             sync.Mutex
	serviceCert      string
	localpk          string
	FuwuMemList      map[string]map[string]model.Fuwu
	startHealthCheck bool
	common.CommonService
}

//todo 发现的新服务不存放到本地内存，替换为存到redis/redis-cluster
func (f *FwfxService) Fxfw(ctx context.Context, fx *fwfx.FuwufaxianPb) (*fwfx.FuwufaxianRespone, error) {
	log.Log.Info("发现了一个新服务：", fx.RemoteAddr)
	fwargs := strings.Split(fx.RemoteAddr, "/")
	if len(fwargs) != 2 {
		log.Log.Error("发现了一个错误的服务！", fx.RemoteAddr)
		return &fwfx.FuwufaxianRespone{
			IsSuccess: false,
		}, nil
	}
	types := fwargs[1]
	selfid := utils.Sha256Encode(fx.RemoteAddr)
	f.lock.Lock()
	var m1 map[string]model.Fuwu
	var ok bool
	if m1, ok = f.FuwuMemList[types]; !ok {
		m1 = make(map[string]model.Fuwu)
	}
	m1[selfid] = model.Fuwu{fx.RemoteAddr, fx.RemoteCert}
	f.FuwuMemList[types] = m1
	f.lock.Unlock()
	if !f.startHealthCheck {
		f.startHealthCheck = true
		go f.healthCheck()
	}
	return &fwfx.FuwufaxianRespone{
		IsSuccess: true,
		SelfId:    selfid,
	}, nil
}

func (f *FwfxService) FuwuList(ctx context.Context, req *fwfx.ListRequest) (*fwfx.ListResult, error) {
	//其他服务向服务发现获取当前已存在的服务列表
	t := req.QueryTypes
	result := new(fwfx.ListResult)
	if t == "*" {
		bs, _ := json.Marshal(f.FuwuMemList)
		result.ListBytes = bs
	} else if m1, ok := f.FuwuMemList[t]; ok {
		m2 := make(map[string]map[string]model.Fuwu)
		m2[t] = m1
		bs, _ := json.Marshal(m2)
		result.ListBytes = bs
	} else {
		log.Log.Errorf("要查询的服务类型[%s]不存在或者暂时还未注册！\n", t)
		return nil, errors.New("要查询的服务类型不存在或者暂时还未注册！")
	}
	return result, nil
}

func (f *FwfxService) Exec(ctx context.Context, req *fwfx.ExecRequest) (*fwfx.ExecResult, error) {
	//业务代码执行时，通过服务发现将请求转发至不同的服务内
	/**
	* 针对是否存在gateway，也需要进行不同的处理。
	* 不存在gateway的时候，由服务发现将各个业务服务的请求转发至对应服务，转发规则是随机获取
	* 存在gateway的时候，将信息转发至gateway，然后由gateway进行权重比较并转发至对应服务
	* 但是无论如何，都需要先进行auth的验证
	**/
	var err error
	gatewayip, err := f.getRandomFuwuByType("gateway")
	if err == nil && gatewayip != "" {
		log.Log.Infof("向gateway[%s]发送请求！", gatewayip)
		cg, err := gtewayutils.GenerateGatewayClients(gatewayip)
		if err != nil {
			return &fwfx.ExecResult{
				Payload: []byte("失败！"),
			}, err
		}
		result, err := cg.GatewayExec(req.Params, req.Token, req.ExecUrl)
		if err != nil {
			return &fwfx.ExecResult{
				Payload: []byte("失败！"),
			}, err
		} else {
			return &fwfx.ExecResult{
				Payload: result,
			}, nil
		}
	}

	ip, err := f.getRandomFuwuByType("auths")
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	ac, err := authutils.GenerateAuthClient(ip)
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	err = ac.CheckAuths(req.Token, req.ExecUrl)
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	//此处url的类型必定是：type/xxx
	//url不带任何IP地址和端口号，不限制访问的服务
	//所以split以后，下标为0的必然是类型
	types := strings.Split(req.ExecUrl, "/")[0]
	execip, err := f.getRandomFuwuByType(types)
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	//fmt.Println("向服务：", execip, "发送请求", ep.Execurl)
	bc, err := businessutil.GenerateBussClients(execip)
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	result, err := bc.ExecBusiness2Bus(req.Params, req.Token, req.ExecUrl)
	if err != nil {
		return &fwfx.ExecResult{
			Payload: []byte("失败！"),
		}, err
	}
	return &fwfx.ExecResult{
		Payload: result,
	}, nil
}

func (f *FwfxService) getRandomFuwuByType(types string) (string, error) {
	l1 := f.FuwuMemList[types]
	lenth := len(l1)
	if lenth == 0 {
		return "", errors.New("当前类型没有已注册的服务！")
	}
	ind := commonutil.GetRandomInt(lenth)
	for _, o := range l1 {
		if ind != 0 {
			ind--
			continue
		}
		//return o.RemoteIp, nil
		return strings.Split(o.RemoteIp, "/")[0], nil
	}
	return "", nil
}

func (f *FwfxService) healthCheck() {
	for {
		if len(f.FuwuMemList) == 0 {
			f.startHealthCheck = false
			break
		}
		for allk, o := range f.FuwuMemList {
			if len(o) == 0 {
				f.lock.Lock()
				delete(f.FuwuMemList, allk)
				f.lock.Unlock()
			}
			for k, o1 := range o {
				cb, err := commonutil.GenerateMicroClients(strings.Split(o1.RemoteIp, "/")[0])
				if err != nil {
					log.Log.Errorf("服务%s健康检查发生错误，错误为%s", o1.RemoteIp, err.Error())
					f.lock.Lock()
					delete(o, k)
					f.FuwuMemList[allk] = o
					f.lock.Unlock()
				}
				err = cb.HealthCheck()
				if err != nil {
					log.Log.Errorf("服务%s健康检查发生错误，错误为%s", o1.RemoteIp, err.Error())
					f.lock.Lock()
					delete(o, k)
					f.FuwuMemList[allk] = o
					f.lock.Unlock()
				}
				log.Log.Debugf("服务%s健康状态正常！", o1.RemoteIp)
			}
		}
		<-time.After(time.Second * 5)
	}
}
